test-gpg-signed-commit \
test-admin-deploy-1 \
test-admin-deploy-2 \
+ test-admin-deploy-etcmerge-cornercases \
test-admin-deploy-uboot \
test-setuid \
test-xattrs \
endif
libgsystem_srcpath := src/libgsystem
-libgsystem_cflags = $(OT_INTERNAL_GIO_UNIX_CFLAGS) -I$(srcdir)/src/libgsystem
+libgsystem_cflags = $(OT_INTERNAL_GIO_UNIX_CFLAGS) -I$(srcdir)/src/libgsystem -DGSYSTEM_CONFIG_XATTRS
libgsystem_libs = $(OT_INTERNAL_GIO_UNIX_LIBS)
include src/libgsystem/Makefile-libgsystem.am
noinst_LTLIBRARIES += libgsystem.la
-Subproject commit f861ba48955b6c3a3a05cdadae510695db3b5a99
+Subproject commit 3a59dab5ed8b574d27c58d967df203825afd0954
GCancellable *cancellable,
GError **error);
-gboolean
-_ostree_set_xattrs_fd (int fd,
- GVariant *xattrs,
- GCancellable *cancellable,
- GError **error);
-
-gboolean _ostree_set_xattrs (GFile *f, GVariant *xattrs,
- GCancellable *cancellable, GError **error);
-
gboolean
_ostree_make_temporary_symlink_at (int tmp_dirfd,
const char *target,
#include <stdio.h>
#include <stdlib.h>
#include <gio/gfiledescriptorbased.h>
-#include <attr/xattr.h>
#include "ostree.h"
#include "ostree-core-private.h"
#include "ostree-chain-input-stream.h"
return ret;
}
-static char *
-canonicalize_xattrs (char *xattr_string, size_t len)
-{
- char *p;
- GSList *xattrs = NULL;
- GSList *iter;
- GString *result;
-
- result = g_string_new (0);
-
- p = xattr_string;
- while (p < xattr_string+len)
- {
- xattrs = g_slist_prepend (xattrs, p);
- p += strlen (p) + 1;
- }
-
- xattrs = g_slist_sort (xattrs, (GCompareFunc) strcmp);
- for (iter = xattrs; iter; iter = iter->next) {
- g_string_append (result, iter->data);
- g_string_append_c (result, '\0');
- }
-
- g_slist_free (xattrs);
- return g_string_free (result, FALSE);
-}
-
-static gboolean
-read_xattr_name_array (const char *path,
- const char *xattrs,
- size_t len,
- GVariantBuilder *builder,
- GError **error)
-{
- gboolean ret = FALSE;
- const char *p;
-
- p = xattrs;
- while (p < xattrs+len)
- {
- ssize_t bytes_read;
- char *buf;
- gs_unref_bytes GBytes *bytes = NULL;
-
- bytes_read = lgetxattr (path, p, NULL, 0);
- if (bytes_read < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- g_prefix_error (error, "lgetxattr (%s, %s) failed: ", path, p);
- goto out;
- }
- if (bytes_read == 0)
- continue;
-
- buf = g_malloc (bytes_read);
- bytes = g_bytes_new_take (buf, bytes_read);
- if (lgetxattr (path, p, buf, bytes_read) < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- g_prefix_error (error, "lgetxattr (%s, %s) failed: ", path, p);
- goto out;
- }
-
- g_variant_builder_add (builder, "(@ay@ay)",
- g_variant_new_bytestring (p),
- ot_gvariant_new_ay_bytes (bytes));
-
- p = p + strlen (p) + 1;
- }
-
- ret = TRUE;
- out:
- return ret;
-}
-
-/**
- * ostree_get_xattrs_for_file:
- * @f: a #GFile
- * @out_xattrs: (out): A new #GVariant containing the extended attributes
- * @cancellable: Cancellable
- * @error: Error
- *
- * Read all extended attributes of @f in a canonical sorted order, and
- * set @out_xattrs with the result.
- *
- * If the filesystem does not support extended attributes, @out_xattrs
- * will have 0 elements, and this function will return successfully.
- */
-gboolean
-ostree_get_xattrs_for_file (GFile *f,
- GVariant **out_xattrs,
- GCancellable *cancellable,
- GError **error)
-{
- gboolean ret = FALSE;
- const char *path;
- ssize_t bytes_read;
- gs_unref_variant GVariant *ret_xattrs = NULL;
- gs_free char *xattr_names = NULL;
- gs_free char *xattr_names_canonical = NULL;
- GVariantBuilder builder;
- gboolean builder_initialized = FALSE;
-
- path = gs_file_get_path_cached (f);
-
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ayay)"));
- builder_initialized = TRUE;
-
- bytes_read = llistxattr (path, NULL, 0);
-
- if (bytes_read < 0)
- {
- if (errno != ENOTSUP)
- {
- ot_util_set_error_from_errno (error, errno);
- g_prefix_error (error, "llistxattr (%s) failed: ", path);
- goto out;
- }
- }
- else if (bytes_read > 0)
- {
- xattr_names = g_malloc (bytes_read);
- if (llistxattr (path, xattr_names, bytes_read) < 0)
- {
- ot_util_set_error_from_errno (error, errno);
- g_prefix_error (error, "llistxattr (%s) failed: ", path);
- goto out;
- }
- xattr_names_canonical = canonicalize_xattrs (xattr_names, bytes_read);
-
- if (!read_xattr_name_array (path, xattr_names_canonical, bytes_read, &builder, error))
- goto out;
- }
-
- ret_xattrs = g_variant_builder_end (&builder);
- g_variant_ref_sink (ret_xattrs);
-
- ret = TRUE;
- ot_transfer_out_value (out_xattrs, &ret_xattrs);
- out:
- if (!builder_initialized)
- g_variant_builder_clear (&builder);
- return ret;
-}
-
static GVariant *
file_header_new (GFileInfo *file_info,
GVariant *xattrs)
if (objtype == OSTREE_OBJECT_TYPE_FILE)
{
- if (!ostree_get_xattrs_for_file (f, &xattrs, cancellable, error))
+ if (!gs_file_get_all_xattrs (f, &xattrs, cancellable, error))
goto out;
}
return ret_metadata;
}
-gboolean
-_ostree_set_xattrs_fd (int fd,
- GVariant *xattrs,
- GCancellable *cancellable,
- GError **error)
-{
- gboolean ret = FALSE;
- int i, n;
-
- n = g_variant_n_children (xattrs);
- for (i = 0; i < n; i++)
- {
- const guint8* name;
- gs_unref_variant GVariant *value = NULL;
- const guint8* value_data;
- gsize value_len;
- int res;
-
- g_variant_get_child (xattrs, i, "(^&ay@ay)",
- &name, &value);
- value_data = g_variant_get_fixed_array (value, &value_len, 1);
-
- do
- res = fsetxattr (fd, (char*)name, (char*)value_data, value_len, 0);
- while (G_UNLIKELY (res == -1 && errno == EINTR));
- if (G_UNLIKELY (res == -1))
- {
- ot_util_set_error_from_errno (error, errno);
- goto out;
- }
- }
-
- ret = TRUE;
- out:
- return ret;
-}
-
-/*
- * _ostree_set_xattrs:
- * @f: a file
- * @xattrs: Extended attribute list
- * @cancellable: Cancellable
- * @error: Error
- *
- * For each attribute in @xattrs, replace the value (if any) of @f for
- * that attribute. This function does not clear other existing
- * attributes.
- */
-gboolean
-_ostree_set_xattrs (GFile *f,
- GVariant *xattrs,
- GCancellable *cancellable,
- GError **error)
-{
- const char *path;
- gboolean ret = FALSE;
- int i, n;
-
- path = gs_file_get_path_cached (f);
-
- n = g_variant_n_children (xattrs);
- for (i = 0; i < n; i++)
- {
- const guint8* name;
- GVariant *value;
- const guint8* value_data;
- gsize value_len;
- gboolean loop_err;
-
- g_variant_get_child (xattrs, i, "(^&ay@ay)",
- &name, &value);
- value_data = g_variant_get_fixed_array (value, &value_len, 1);
-
- loop_err = lsetxattr (path, (char*)name, (char*)value_data, value_len, 0) < 0;
- g_clear_pointer (&value, (GDestroyNotify) g_variant_unref);
- if (loop_err)
- {
- ot_util_set_error_from_errno (error, errno);
- g_prefix_error (error, "lsetxattr (%s, %s) failed: ", path, name);
- goto out;
- }
- }
-
- ret = TRUE;
- out:
- return ret;
-}
-
/* Create a randomly-named symbolic link in @tempdir which points to
* @target. The filename will be returned in @out_file.
*
gchar **out_checksum,
OstreeObjectType *out_objtype);
-gboolean ostree_get_xattrs_for_file (GFile *f,
- GVariant **out_xattrs,
- GCancellable *cancellable,
- GError **error);
-
gboolean
ostree_content_stream_parse (gboolean compressed,
GInputStream *input,
if (xattrs)
{
- if (!_ostree_set_xattrs_fd (fd, xattrs, cancellable, error))
+ if (!gs_fd_set_all_xattrs (fd, xattrs, cancellable, error))
goto out;
}
}
if (xattrs)
{
gs_unref_object GFile *path = g_file_get_child (destination_parent, destination_name);
- if (!_ostree_set_xattrs (path, xattrs, cancellable, error))
+ if (!gs_file_set_all_xattrs (path, xattrs, cancellable, error))
goto out;
}
}
if (xattrs)
{
gs_unref_object GFile *temp_path = g_file_get_child (destination_parent, temp_filename);
- if (!_ostree_set_xattrs (temp_path, xattrs, cancellable, error))
+ if (!gs_file_set_all_xattrs (temp_path, xattrs, cancellable, error))
goto out;
}
}
if (xattrs)
{
- if (!_ostree_set_xattrs_fd (destination_dfd, xattrs, cancellable, error))
+ if (!gs_fd_set_all_xattrs (destination_dfd, xattrs, cancellable, error))
goto out;
}
}
*/
if (xattrs != NULL)
{
- if (!_ostree_set_xattrs (temp_file, xattrs, cancellable, error))
+ if (!gs_file_set_all_xattrs (temp_file, xattrs, cancellable, error))
goto out;
}
}
if (xattrs)
{
- if (!_ostree_set_xattrs_fd (fd, xattrs, cancellable, error))
+ if (!gs_fd_set_all_xattrs (fd, xattrs, cancellable, error))
goto out;
}
}
g_debug ("Adding: %s", gs_file_get_path_cached (dir));
if (!(modifier && (modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS) > 0))
{
- if (!ostree_get_xattrs_for_file (dir, &xattrs, cancellable, error))
+ if (!gs_file_get_all_xattrs (dir, &xattrs, cancellable, error))
goto out;
}
if (!(modifier && (modifier->flags & OSTREE_REPO_COMMIT_MODIFIER_FLAGS_SKIP_XATTRS) > 0))
{
g_clear_pointer (&xattrs, (GDestroyNotify) g_variant_unref);
- if (!ostree_get_xattrs_for_file (child, &xattrs, cancellable, error))
+ if (!gs_file_get_all_xattrs (child, &xattrs, cancellable, error))
goto out;
}
gs_unref_object GFile *full_path =
_ostree_repo_get_object_path (self, checksum, OSTREE_OBJECT_TYPE_FILE);
- if (!ostree_get_xattrs_for_file (full_path, &ret_xattrs,
- cancellable, error))
+ if (!gs_file_get_all_xattrs (full_path, &ret_xattrs,
+ cancellable, error))
goto out;
}
#include "config.h"
#include "ostree-sysroot-private.h"
+#include "ostree-core-private.h"
#include "otutil.h"
#include "libgsystem.h"
/**
- * copy_one_config_file:
+ * copy_modified_config_file:
*
* Copy @file from @modified_etc to @new_etc, overwriting any existing
- * file there.
+ * file there. The @file may refer to a regular file, a symbolic
+ * link, or a directory. Directories will be copied recursively.
+ *
+ * Note this function does not (yet) handle the case where a directory
+ * needed by a modified file is deleted in a newer tree.
*/
static gboolean
-copy_one_config_file (GFile *orig_etc,
- GFile *modified_etc,
- GFile *new_etc,
- GFile *src,
- GCancellable *cancellable,
- GError **error)
+copy_modified_config_file (GFile *orig_etc,
+ GFile *modified_etc,
+ GFile *new_etc,
+ GFile *src,
+ GCancellable *cancellable,
+ GError **error)
{
gboolean ret = FALSE;
gs_unref_object GFileInfo *src_info = NULL;
+ gs_unref_object GFileInfo *parent_info = NULL;
gs_unref_object GFile *dest = NULL;
- gs_unref_object GFile *parent = NULL;
+ gs_unref_object GFile *dest_parent = NULL;
gs_free char *relative_path = NULL;
relative_path = g_file_get_relative_path (modified_etc, src);
if (!src_info)
goto out;
- if (g_file_info_get_file_type (src_info) == G_FILE_TYPE_DIRECTORY)
+ dest_parent = g_file_get_parent (dest);
+ if (!ot_gfile_query_info_allow_noent (dest_parent, OSTREE_GIO_FAST_QUERYINFO, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ &parent_info, cancellable, error))
+ goto out;
+ if (!parent_info)
{
- gs_unref_object GFileEnumerator *src_enum = NULL;
- gs_unref_object GFileInfo *child_info = NULL;
- GError *temp_error = NULL;
-
- /* FIXME actually we need to copy permissions and xattrs */
- if (!gs_file_ensure_directory (dest, TRUE, cancellable, error))
- goto out;
-
- src_enum = g_file_enumerate_children (src, OSTREE_GIO_FAST_QUERYINFO,
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
- cancellable, error);
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+ "New tree removes parent directory '%s', cannot merge",
+ gs_file_get_path_cached (dest_parent));
+ goto out;
+ }
- while ((child_info = g_file_enumerator_next_file (src_enum, cancellable, error)) != NULL)
- {
- gs_unref_object GFile *child = g_file_get_child (src, g_file_info_get_name (child_info));
+ if (!gs_shutil_rm_rf (dest, cancellable, error))
+ goto out;
- if (!copy_one_config_file (orig_etc, modified_etc, new_etc, child,
- cancellable, error))
- goto out;
- }
- g_clear_object (&child_info);
- if (temp_error != NULL)
- {
- g_propagate_error (error, temp_error);
- goto out;
- }
+ if (g_file_info_get_file_type (src_info) == G_FILE_TYPE_DIRECTORY)
+ {
+ if (!gs_shutil_cp_a (src, dest, cancellable, error))
+ goto out;
}
else
{
- parent = g_file_get_parent (dest);
-
- /* FIXME actually we need to copy permissions and xattrs */
- if (!gs_file_ensure_directory (parent, TRUE, cancellable, error))
- goto out;
-
- /* We unlink here because otherwise gio throws an error on
- * dangling symlinks.
- */
- if (!ot_gfile_ensure_unlinked (dest, cancellable, error))
- goto out;
-
if (!g_file_copy (src, dest, G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS | G_FILE_COPY_ALL_METADATA,
cancellable, NULL, NULL, error))
goto out;
{
OstreeDiffItem *diff = modified->pdata[i];
- if (!copy_one_config_file (orig_etc, modified_etc, new_etc, diff->target,
- cancellable, error))
+ if (!copy_modified_config_file (orig_etc, modified_etc, new_etc, diff->target,
+ cancellable, error))
goto out;
}
for (i = 0; i < added->len; i++)
{
GFile *file = added->pdata[i];
- if (!copy_one_config_file (orig_etc, modified_etc, new_etc, file,
- cancellable, error))
+ if (!copy_modified_config_file (orig_etc, modified_etc, new_etc, file,
+ cancellable, error))
goto out;
}
--- /dev/null
+#!/bin/bash
+#
+# Copyright (C) 2013 Colin Walters <walters@verbum.org>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+set -e
+
+. $(dirname $0)/libtest.sh
+
+echo "1..1"
+
+setup_os_repository "archive-z2" "syslinux"
+
+echo "ok setup"
+
+echo "1..1"
+
+ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmaster/x86_64-runtime
+rev=$(ostree --repo=sysroot/ostree/repo rev-parse testos/buildmaster/x86_64-runtime)
+export rev
+echo "rev=${rev}"
+# This initial deployment gets kicked off with some kernel arguments
+ostree admin --sysroot=sysroot deploy --karg=root=LABEL=MOO --karg=quiet --os=testos testos:testos/buildmaster/x86_64-runtime
+assert_has_dir sysroot/boot/ostree/testos-${bootcsum}
+
+# Ok, let's create a long directory chain with custom permissions
+etc=sysroot/ostree/deploy/testos/deploy/${rev}.0/etc
+mkdir -p ${etc}/a/long/dir/chain
+mkdir -p ${etc}/a/long/dir/forking
+chmod 700 ${etc}/a
+chmod 770 ${etc}/a/long
+chmod 777 ${etc}/a/long/dir
+chmod 707 ${etc}/a/long/dir/chain
+chmod 700 ${etc}/a/long/dir/forking
+
+# Now deploy a new commit
+os_repository_new_commit
+ostree --repo=sysroot/ostree/repo remote add --set=gpg-verify=false testos file://$(pwd)/testos-repo testos/buildmaster/x86_64-runtime
+ostree admin --sysroot=sysroot upgrade --os=testos
+newrev=$(ostree --repo=sysroot/ostree/repo rev-parse testos/buildmaster/x86_64-runtime)
+echo "newrev=${newrev}"
+
+newetc=sysroot/ostree/deploy/testos/deploy/${newrev}.0/etc
+assert_file_has_mode() {
+ stat -c '%a' $1 > mode.txt
+ if ! grep -q -e "$2" mode.txt; then
+ echo 1>&2 "file $1 has mode $(cat mode.txt); expected $2";
+ exit 1
+ fi
+ rm -f mode.txt
+}
+assert_file_has_mode ${newetc}/a 700
+assert_file_has_mode ${newetc}/a/long 770
+assert_file_has_mode ${newetc}/a/long/dir 777
+assert_file_has_mode ${newetc}/a/long/dir/chain 707
+assert_file_has_mode ${newetc}/a/long/dir/forking 700
+assert_file_has_mode ${newetc}/a/long/dir 777
+
+echo "ok"